package xyz.zcq.springframework.beans.factory.support;

import xyz.zcq.springframework.beans.BeanException;
import xyz.zcq.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;

/**
 * 抽象的有能力自动注入的 bean 工厂
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

//    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Object bean;

        // 创建 bean 实例
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeanException("Instantiation of bean failed", e);
        }

        // 注册单例 bean
        registerSingleton(beanName, bean);

        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws InstantiationException, IllegalAccessException {
        if (args == null) {
            return instantiationStrategy.instantiate(beanDefinition, beanName, null, null);
        }
        Constructor<?> constructor = null;

        for (Constructor<?> ctor : beanDefinition.getBeanClass().getDeclaredConstructors()) {
            if (ctor.getParameterTypes().length == args.length) {
                Class<?>[] parameterTypes = ctor.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (args[i] != null && parameterTypes[i] != null
                            && parameterTypes[i].isAssignableFrom(args[i].getClass())) {
                        constructor = ctor;
                        break;
                    }
                }
            }
        }

        return instantiationStrategy.instantiate(beanDefinition, beanName, constructor, args);
    }

}
